home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Turnbull China Bikeride
/
Turnbull China Bikeride - Disc 2.iso
/
BARNET
/
ARMTEX
/
EXTRAS
/
DRAW2LAT
/
Source
/
cc
/
draw2latex
Wrap
Text File
|
1998-10-18
|
40KB
|
1,282 lines
// -*- C++ -*-
/* $Id: cc.draw2latex 1.8 1998/10/18 15:10:15 atterer Exp $
__ _
|_) /| Copyright Richard Atterer
| \/¯| <atterer@augsburg.baynet.de>
¯ ´` ¯
Conversion of Draw files into LaTeX (picture env)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
_________________________________________________________________________
$Log: cc.draw2latex $
Revision 1.8 1998/10/18 15:10:15 atterer
command line switches; released as V1
Revision 1.7 1998/10/17 11:15:27 atterer
extract comments from text areas, substitute '#1' to '#8'
Revision 1.6 1998/09/08 21:10:42 atterer
arrowheads positioned correctly for diagonal lines
very basic circle support
Revision 1.5 1998/09/05 00:40:10 atterer
arrowheads (as seperate \vector commands)
Revision 1.4 1998/09/02 23:42:38 atterer
Text labels (with extra commands for Trinity/Homerton/Corpus as well as
Oblique/Bold), Grouped objects of a box and a text label turned into one
box with text centered
Revision 1.3 1998/08/31 22:50:05 atterer
recognizes boxes
Revision 1.2 1998/08/30 18:55:31 atterer
handling of short straight lines, output of numbers with supressed
zeroes after decimal point.
Revision 1.1 1998/08/29 16:43:21 atterer
Initial revision
*/
#include <kernel.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <std/typeinfo.h>
static const double pi = 3.141592654;
inline double dabs(double n) {
return (n > 0 ? n : -n);
}
inline int iabs(int n) {
return (n > 0 ? n : -n);
}
extern const char* const rcsid =
"$Id: cc.draw2latex 1.8 1998/10/18 15:10:15 atterer Exp $";
#ifdef DEBUG
#define D(cmd) cmd
#define ND(cmd)
#define log(args) printf args
#else
#define D(cmd)
#define ND(cmd) cmd
#define log(args)
#endif
//______________________________________________________________________
// switches
// true => turn short straight lines into beziers, false => make longer
static bool shortbezline = false;
static const double shortlinelen = 10;
// true => (0,0) in {picture} is lower left of global bbox of drawfile,
// false => (0,0) in {picture} is (0,0) in drawfile.
static bool usedrawbbox = true;
//____________________
typedef signed coord;
typedef unsigned word;
struct XY {
coord x, y;
bool near(const XY& p) const {
return dabs(x - p.x) < 8 * 256
&& dabs(y - p.y) < 8 * 256;
}
void avg(const XY& a, const XY& b) {
x = (a.x + b.x) / 2;
y = (a.y + b.y) / 2;
}
XY(void) { } // don't initialise
XY(coord cx, coord cy) : x(cx), y(cy) { }
};
enum thinthick { neither, thin, thick };
static int print_thinthick(FILE* file, thinthick newt);
//________________________________________
struct BBox {
int load(FILE* file) {
return fread(&min.x, sizeof(coord), 4, file); // ahem, not very nice...
}
void set_from_mem(const void* ptr) {
*this = *(BBox*)ptr; // AHEM...
}
// alter bbox so that other box is included
void merge(const BBox* other) {
if (min.x > other->min.x) min.x = other->min.x;
if (min.y > other->min.y) min.y = other->min.y;
if (max.x < other->max.x) max.x = other->max.x;
if (max.y < other->max.y) max.y = other->max.y;
}
// min inclusive, max exclusive
XY min, max;
};
//________________________________________
// base class for draw objects
class Drawfile;
class DrawObj {
public:
DrawObj(Drawfile* p) : next(0), parent(p) { }
virtual ~DrawObj() { }
/* read object definition from file - file pointer points to word after
the object type field. returns zero for success */
virtual int load(FILE* file) = 0;
// output LaTeX
virtual int latex(FILE* file, const BBox& globbox) const = 0;
virtual thinthick thinstate(void) = 0;
void setnext(DrawObj* obj) { next = obj; return; }
DrawObj* getnext() const { return next; }
void setparent(Drawfile* f) { parent = f; return; }
Drawfile* getparent() const { return parent; }
const BBox* getbbox() const { return &bbox; }
protected:
BBox bbox;
DrawObj* next;
Drawfile* parent;
};
//____________________
class Path : public DrawObj {
public:
Path(Drawfile* p) : point(0), element(0), DrawObj(p) { }
int load(FILE* file);
int latex(FILE* file, const BBox& globbox) const;
thinthick thinstate(void) { return thinl; }
static void setlabel(const char* l1, const char* l2) {
label1 = const_cast<char*>(l1);
label2 = const_cast<char*>(l2);
}
static bool usedlabel() { return label1 == 0; }
~Path() {
if (point != 0) free(point);
if (element != 0) free(element);
}
private:
thinthick thinl;
bool sarrow, earrow; // arrowhead at start/end?
enum elemtype { move, close, draw, bezier };
struct tag {
elemtype type;
int firstpoint;
};
static char* label1;
static char* label2;
int points; // nr of entries in point[]
XY* point;
int elements; // nr of entries in element[] and firstpoint[]
tag* element;
};
//____________________
class Label : public DrawObj {
public:
Label(Drawfile* p) : text(0), DrawObj(p) { }
int load(FILE* file);
int latex(FILE* file, const BBox& globbox) const;
thinthick thinstate(void) { return neither; }
const char* fontcmd(void) const;
const char* getlabel() { return text; }
~Label() { if (text) delete text; }
private:
char* text;
int fontnr;
};
//____________________
class Group : public DrawObj {
public:
Group(Drawfile* p) : first(0), DrawObj(p) { }
int load(FILE* file);
int latex(FILE* file, const BBox& globbox) const;
thinthick thinstate(void) { return neither; }
~Group();
private:
DrawObj* first;
};
//____________________
class Textarea : public DrawObj {
public:
Textarea(Drawfile* p) : text(0), DrawObj(p) { }
int load(FILE* file);
int latex(FILE* file, const BBox& globbox) const;
thinthick thinstate(void) { return thin; }
~Textarea();
private:
char* text;
};
//________________________________________
enum fonttype { nofont = -1,
roman = 0, roman_i = 1, roman_b = 2, roman_ib = 3,
sanss = 4, sanss_i = 5, sanss_b = 6, sanss_ib = 7,
typew = 8, typew_i = 9, typew_b = 10, typew_ib = 11 };
class Drawfile {
public:
// object type numbers as stored in Draw files
enum objtype {
type_fonttable = 0,
type_label = 1, // Text object
type_path = 2,
type_group = 6,
type_textarea = 9,
type_transformlabel = 12 };
// set up Drawfile data structure from file
int load(FILE* file);
// output in LaTeX format, using {picture}
int latex(FILE* file) const;
// create bbox data from that of all objects in the Drawfile
void make_bbox(void);
static int latex_line(FILE* file, coord x0, coord y0, coord x1, coord y1);
static coord latex_lineerrx(void) { return lineerr.x; }
static coord latex_lineerry(void) { return lineerr.y; }
static int latex_arrowhead(FILE* file, coord x, coord y, double angle);
~Drawfile();
static int loadobj(FILE* file, DrawObj*& dest, Drawfile* destfile);
int loadfonttable(FILE* file);
fonttype fontentry(int idx) { return fonttable[idx]; }
private:
static inline void Drawfile::cmp_angle(double& min, double angle,
double& best, signed& bestoutx, signed& bestouty,
signed outx, signed outy, double outangle);
static XY lineerr;
fonttype fonttable[256];
BBox bbox;
DrawObj* first;
};
//______________________________________________________________________
//______________________________________________________________________
Drawfile::~Drawfile(void)
{
DrawObj* o = first;
while (o) {
DrawObj* n = o->getnext();
log(("~Drawfile: deleting %x, next at %x\n", (int)o, (int)n));
delete o;
o = n;
}
return;
}
//________________________________________
int Drawfile::loadfonttable(FILE* file)
{
word size;
long pos = ftell(file);
if (pos == -1L) return 1;
if (fread(&size, sizeof(word), 1, file) == 0) return 1;
pos += size - sizeof(word);
// load font names until size reached
char name[512];
long currpos;
while ((currpos = ftell(file)) != -1L && currpos < pos - 3
&& feof(file) == 0) {
int idx = getc(file);
if (idx == EOF) return 1;
int c;
char* nameptr = name;
while ((c = getc(file)) >= ' ') *nameptr++ = c;
*nameptr = 0;
log(("Drawfile::loadfonttable: pos=%ld, currpos=%ld, #%d=\"%s\"\n", pos, currpos, idx, name));
if (strncmp(name, "Trinity", 7) == 0)
fonttable[idx] = roman;
else if (strncmp(name, "Homerton", 8) == 0)
fonttable[idx] = sanss;
else if (strncmp(name, "Corpus", 6) == 0)
fonttable[idx] = typew;
if (fonttable[idx] != nofont) {
if (strstr(name, ".Italic") || strstr(name, ".Oblique"))
fonttable[idx] = (fonttype)(int(fonttable[idx]) | 1);
if (strstr(name, ".Bold"))
fonttable[idx] = (fonttype)(int(fonttable[idx]) | 2);
}
}
log(("Drawfile::loadfonttable: end, pos=%ld, currpos=%ld\n", pos, currpos));
if (fseek(file, pos, SEEK_SET)) return 1; else return 0;
}
//________________________________________
int Drawfile::loadobj(FILE* file, DrawObj*& dest, Drawfile* destfile)
{
log(("Drawfile::loadobj\n"));
// get one word of object type
bool read = false; // may have to skip unknown objects
while (!read) {
word objtypeword;
if (fread(&objtypeword, sizeof(word), 1, file) == 0) return 2;
//if (feof(file)) return 2;
// read object
objtype curobjtype = (objtype)objtypeword;
log(("Drawfile::loadobj: objtype %d\n", curobjtype));
switch (curobjtype) {
case type_fonttable:
if (destfile == 0 || destfile->loadfonttable(file)) return 1;
break;
case type_label: {
dest = new Label(destfile);
if (dest->load(file)) { delete dest; dest = 0; return 1; }
read = true;
break;
}
case type_path: {
dest = new Path(destfile);
if (dest->load(file)) { delete dest; dest = 0; return 1; }
read = true;
break;
}
case type_group: {
dest = new Group(destfile);
if (dest->load(file)) { delete dest; dest = 0; return 1; }
read = true;
break;
}
case type_textarea: {
dest = new Textarea(destfile);
if (dest->load(file)) { delete dest; dest = 0; return 1; }
read = true;
break;
}
default:
// unknown object type; skip object
word len;
if (fread(&len, sizeof(word), 1, file) == 0) {
dest = 0;
return 1;
}
log(("Drawfile::load: unknown obj %d, len %d\n", curobjtype, len));
if (fseek(file, len - 2 * sizeof(word), SEEK_CUR)) {
dest = 0;
return 1;
}
break;
}
}
return 0;
}
//________________________________________
int Drawfile::load(FILE* file)
{
log(("Drawfile::load\n"));
int i = 255;
do {
fonttable[i] = nofont;
} while (--i >= 0);
// read file header
word hdrword;
log(("Drawfile::load to %x\n", (int)&hdrword));
if (fread(&hdrword, sizeof(word), 1, file) == 0) return 1;
if (hdrword != 0x77617244) return 1; // 'Draw'
if (fseek(file, 40, SEEK_SET)) return 1;
//____________________
// read objects
if (loadobj(file, first, this)) return 1;
DrawObj* prev = first;
while (feof(file) == 0) {
DrawObj* currobj;
int err;
if ((err = loadobj(file, currobj, this)) == 2) return 0;
if (err) return 1;
prev->setnext(currobj);
prev = currobj;
}
return 0;
}
//______________________________________________________________________
void Drawfile::make_bbox(void)
{
DrawObj* o = first;
log(("Drawfile::make_bbox: first at %x\n", (int)o));
if (o) {
bbox = *o->getbbox();
o = o->getnext();
}
while (o) {
log(("Drawfile::make_bbox: merging with %x\n", (int)o));
bbox.merge(o->getbbox());
o = o->getnext();
}
if (!usedrawbbox) {
bbox.max.x -= bbox.min.x;
bbox.max.y -= bbox.min.y;
bbox.min.x = 0; bbox.min.y = 0;
return;
}
return;
}
//______________________________________________________________________
char* Path::label1 = 0;
char* Path::label2 = 0;
// load individual objects' data
int Path::load(FILE* file)
{
struct {
word size;
coord xmin, ymin, xmax, ymax;
word fillcol, outlinecol, outlinewidth;
word pathstyle;
} head;
// read obj header
if (fread(&head, sizeof(head), 1, file) == 0) return 1;
bbox.set_from_mem(&head.xmin);
if (head.outlinewidth) thinl = thick; else thinl = thin;
if ((head.pathstyle & 0x30) == 0x30) sarrow = true; else sarrow = false;
if ((head.pathstyle & 0xc) == 0xc) earrow = true; else earrow = false;
if (head.pathstyle & 1<<7) { // skip dash pattern if present
struct { word offset, elements; } dashstart;
if (fread(&dashstart, sizeof(dashstart), 1, file) == 0) return 1;
log(("Path::load: skipping %d dash patterns\n", dashstart.elements));
if (fseek(file, dashstart.elements * sizeof(word), SEEK_CUR)) return 1;
}
point = static_cast<XY*>(malloc(0)); // coordinates
if (point == 0) return 1;
points = 0;
element = static_cast<tag*>(malloc(0)); // tag info
if (element == 0) return 1;
elements = 0;
// read path elements
word tagtype;
if (fread(&tagtype, sizeof(tagtype), 1, file) == 0) return 1;
while ((tagtype &= 0xff) != 0) { // until "end of path"
int coopairs; // nr of x/y pairs following
elemtype thistype;
switch (tagtype) {
case 2: coopairs = 1; thistype = move; break;
case 5: coopairs = 0; thistype = close; break;
case 8: coopairs = 1; thistype = draw; break;
case 6: coopairs = 3; thistype = bezier; break;
default: // just to avoid compiler warnings
coopairs = 0; thistype = (elemtype)0;
return 1;
}
log(("Path::load: tag type %d; %d coords follow\n", tagtype, coopairs));
// enlarge element[]
elements++;
tag* e = (tag*)realloc(element, elements * sizeof(tag));
if (e == 0) return 1;
element = e;
element[elements - 1].type = thistype;
element[elements - 1].firstpoint = points;
// enlarge point[]
if (coopairs) {
points += coopairs;
XY* p = (XY*)realloc(point, points * sizeof(XY));
if (p == 0) return 1;
point = p;
if (fread(&point[points - coopairs], coopairs * sizeof(XY),
1, file) == 0) return 1;
}
if (fread(&tagtype, sizeof(tagtype), 1, file) == 0) return 1;
}
log(("Path::load: returning; %d elements, %d points\n", elements, points));
return 0;
}
//________________________________________
int Label::load(FILE* file)
{
struct {
word size;
coord xmin, ymin, xmax, ymax;
word textcol, backcol, style, xsize, ysize, xbaseline, ybaseline;
} head;
log(("Label::load\n"));
if (fread(&head, sizeof(head), 1, file) == 0) return 1;
head.xmin = head.xbaseline; head.ymin = head.ybaseline;
bbox.set_from_mem(&head.xmin);
int strlen = head.size - sizeof(head) - sizeof(word);
text = new char[strlen];
fontnr = head.style & 0xff;
if (text == 0) return 1;
if (fread(text, sizeof(char), strlen, file) == 0) return 1;
return 0;
}
//________________________________________
Group::~Group(void)
{
DrawObj* o = first;
while (o) {
DrawObj* n = o->getnext();
log(("~Group: deleting %x, next at %x\n", (int)o, (int)n));
delete o;
o = n;
}
return;
}
//________________________________________
int Group::load(FILE* file)
{
struct {
word size;
coord xmin, ymin, xmax, ymax;
char description[12];
} head;
log(("Group::load\n"));
long pos = ftell(file);
if (pos == -1L) return 1;
// read obj header
if (fread(&head, sizeof(head), 1, file) == 0) return 1;
bbox.set_from_mem(&head.xmin);
pos += head.size - sizeof(word);
// load objects until size reached
if (Drawfile::loadobj(file, first, parent)) return 1;
DrawObj* prev = first;
long currpos;
while ((currpos = ftell(file)) != -1L && currpos < pos
&& feof(file) == 0) {
log(("Group::load: pos=%ld, currpos=%ld\n", pos, currpos));
DrawObj* currobj;
int err;
if ((err = Drawfile::loadobj(file, currobj, parent)) == 2) return 0;
log(("Group::load: obj loaded to %x, prev at %x\n", (int)currobj, (int)prev));
if (err) return 1;
prev->setnext(currobj);
prev = currobj;
}
if (currpos >= pos) return 0; else return 1;
}
//________________________________________
Textarea::~Textarea(void)
{
if (text) free(text);
return;
}
//________________________________________
int Textarea::load(FILE* file)
{
struct {
word size;
coord xmin, ymin, xmax, ymax;
word dummy;
} head;
struct {
word size;
coord xmin, ymin, xmax, ymax;
word objtype; // objtype of following text column obj, or 0
} areaobj;
log(("Textarea::load\n"));
// read obj header
if (fread(&head, sizeof(head), 1, file) == 0) return 1;
bbox.set_from_mem(&head.xmin);
// 4 of the 5 words are skipped below, 1 is for obj type
head.size -= sizeof(head) + 5 * sizeof(word);
// skip text column objects
do {
if (fread(&areaobj, sizeof(areaobj), 1, file) == 0) return 1;
log(("Textarea::load: skip column\n"));
head.size -= sizeof(areaobj);
} while (areaobj.objtype != 0);
// skip other words in header
if (fread(&areaobj, 4 * sizeof(word), 1, file) == 0) return 1;
// now load ASCII textarea source
text = static_cast<char*>(malloc(head.size));
if (text == 0 || fread(text, head.size, 1, file) == 0) return 1;
log(("Textarea::load: size %d\n", head.size));
return 0;
}
//______________________________________________________________________
// convert to LaTeX coordinates
int latex_coord(int len) {
return len * 72 / 180 / 256;
/* NB slightly incorrect; one TeX pt != 1/72"
72.27 TeX pt make one inch */
}
double latex_coord(double len) {
return len * 72 / 180 / 256;
}
double latex_coord1(double len) {
double n = double(int(len * 72 / 180 / 256 * 10 + .5)) / 10;
if (n > .01) return n; else return 0;
}
// convert to Draw coordinates
int coord_latex(double len) {
return int(len / 72 * 180 * 256);
}
double round1(double n) {
double m = double(int(n * 10 + .5)) / 10;
if (m > .01) return m; else return 0;
}
double round2(double n) {
double m = double(int(n * 100 +.5)) / 100;
if (m > .001) return m; else return 0;
}
//________________________________________
// print "\thinlines" or "\thicklines"
int print_thinthick(FILE* file, thinthick newt)
{
static thinthick thinlines = neither;
if (file == 0) {
thinlines = newt;
return 0;
}
if (thinlines != newt) {
switch (newt) {
case neither:
break;
case thin:
if (fprintf(file, "\n\\thinlines") == 0) return 1;
thinlines = newt;
break;
case thick:
if (fprintf(file, "\n\\thicklines") == 0) return 1;
thinlines = newt;
break;
}
}
return 0;
}
//________________________________________
// output LaTeX
int Drawfile::latex(FILE* file) const
{
log(("Drawfile::latex\n"));
print_thinthick(0, neither);
if (fprintf(file, "\\documentclass[12pt]{article}\n\\begin{document}\n")
== 0) return 1;
if (fprintf(file, "\n%% converted by draw2latex"
"\n\\begin{picture}(%d,%d)",
latex_coord(bbox.max.x - bbox.min.x),
latex_coord(bbox.max.y - bbox.min.y))
== 0) return 1;
#if 0
if (fprintf(file, "\n\\put(0,0){\\framebox(%d,%d){}}",
latex_coord(bbox.max.x - bbox.min.x),
latex_coord(bbox.max.y - bbox.min.y))
== 0) return 1;
#endif
DrawObj* o = first;
// output objects
while (o) {
thinthick newthinlines = o->thinstate();
if (print_thinthick(file, newthinlines)) return 1;
if (o->latex(file, bbox)) return 1;
o = o->getnext();
}
if (print_thinthick(file, thin)) return 1;
if (fprintf(file, "\n\\end{picture}\n\n\\end{document}\n")
== 0) return 1;
return 0;
}
//________________________________________
inline void Drawfile::cmp_angle(double& min, double angle, double& best,
signed& bestoutx, signed& bestouty,
signed outx, signed outy, double outangle) {
double tmp;
if (min > (tmp = dabs(angle - outangle))) {
bestoutx = outx; bestouty = outy;
min = tmp;
best = outangle;
}
}
//____________________
/* output a \vector{} command, but with a vector length of 0.
angle is where the vector points to, i.e. 0 is right, pi/2 is up etc.
angle must be from -pi to +pi */
int Drawfile::latex_arrowhead(FILE* file, coord x, coord y, double angle)
{
bool downward, left;
double lx = latex_coord(double(x));
double ly = latex_coord(double(y));
lx += 3.5 * cos(angle); // don't let line overstrike tip of arrowhead
ly += 3.5 * sin(angle);
log(("Drawfile::latex_arrowhead: angle %f (%g,%g)\n", angle, lx, ly));
// restrict angle to 0..90 degrees
if (angle < 0.0) {
downward = true;
angle = -angle;
} else {
downward = false;
}
if (angle > pi / 2) {
left = true;
angle = pi - angle;
} else {
left = false;
}
signed bestoutx = 1, bestouty = 0;
double min = angle; // difference between angle and best angle so far
double best = 0; // best angle so far
/* update best, bestoutx, outy if outangle nearer to angle than for
previous calls. outx,y is vector passed to \line{} */
cmp_angle(min, angle, best, bestoutx, bestouty, 1, 4, 1.32581766);
cmp_angle(min, angle, best, bestoutx, bestouty, 4, 1, 0.244978663);
cmp_angle(min, angle, best, bestoutx, bestouty, 3, 4, 0.927295218);
cmp_angle(min, angle, best, bestoutx, bestouty, 4, 3, 0.643501109);
cmp_angle(min, angle, best, bestoutx, bestouty, 1, 3, 1.24904577);
cmp_angle(min, angle, best, bestoutx, bestouty, 3, 1, 0.321750554);
cmp_angle(min, angle, best, bestoutx, bestouty, 2, 3, 0.982793723);
cmp_angle(min, angle, best, bestoutx, bestouty, 3, 2, 0.588002604);
cmp_angle(min, angle, best, bestoutx, bestouty, 1, 2, 1.10714872);
cmp_angle(min, angle, best, bestoutx, bestouty, 2, 1, 0.463647609);
cmp_angle(min, angle, best, bestoutx, bestouty, 1, 1, 0.785398163);
cmp_angle(min, angle, best, bestoutx, bestouty, 1, 1, 0.785398163);
cmp_angle(min, angle, best, bestoutx, bestouty, 0, 1, pi / 2);
if (left) { bestoutx = -bestoutx; best = pi - best; }
if (downward) { bestouty = -bestouty; best = -best; }
if (fprintf(file, "\n\\put(%.4g,%.4g){\\vector(%d,%d){0}}",
round1(lx), round1(ly), bestoutx, bestouty) == 0) return 1;
return 0;
}
//____________________
XY Drawfile::lineerr;
int Drawfile::latex_line(FILE* file, coord x0, coord y0, coord x1, coord y1)
{
double dx = latex_coord(double(x0 - x1)); // distance
double dy = latex_coord(double(y0 - y1));
// find best angle
double angle = dabs(atan2(dy, dx));
if (angle > pi / 2) angle = pi - angle; // angle 0..90 degrees
signed bestoutx = 1, bestouty = 0;
double min = angle; // difference between angle and best angle so far
double best = 0; // best angle so far
/* update best, bestoutx, outy if outangle nearer to angle than for
previous calls. outx,y is vector passed to \line{} */
cmp_angle(min, angle, best, bestoutx, bestouty, 1, 6, 1.40564765);
cmp_angle(min, angle, best, bestoutx, bestouty, 6, 1, 0.165148677);
cmp_angle(min, angle, best, bestoutx, bestouty, 5, 6, 0.87605805);
cmp_angle(min, angle, best, bestoutx, bestouty, 6, 5, 0.694738276);
cmp_angle(min, angle, best, bestoutx, bestouty, 1, 5, 1.37340077);
cmp_angle(min, angle, best, bestoutx, bestouty, 5, 1, 0.19739556);
cmp_angle(min, angle, best, bestoutx, bestouty, 2, 5, 1.19028995);
cmp_angle(min, angle, best, bestoutx, bestouty, 5, 2, 0.380506377);
cmp_angle(min, angle, best, bestoutx, bestouty, 3, 5, 1.03037683);
cmp_angle(min, angle, best, bestoutx, bestouty, 5, 3, 0.5404195);
cmp_angle(min, angle, best, bestoutx, bestouty, 4, 5, 0.896055384);
cmp_angle(min, angle, best, bestoutx, bestouty, 5, 4, 0.674740942);
cmp_angle(min, angle, best, bestoutx, bestouty, 1, 4, 1.32581766);
cmp_angle(min, angle, best, bestoutx, bestouty, 4, 1, 0.244978663);
cmp_angle(min, angle, best, bestoutx, bestouty, 3, 4, 0.927295218);
cmp_angle(min, angle, best, bestoutx, bestouty, 4, 3, 0.643501109);
cmp_angle(min, angle, best, bestoutx, bestouty, 1, 3, 1.24904577);
cmp_angle(min, angle, best, bestoutx, bestouty, 3, 1, 0.321750554);
cmp_angle(min, angle, best, bestoutx, bestouty, 2, 3, 0.982793723);
cmp_angle(min, angle, best, bestoutx, bestouty, 3, 2, 0.588002604);
cmp_angle(min, angle, best, bestoutx, bestouty, 1, 2, 1.10714872);
cmp_angle(min, angle, best, bestoutx, bestouty, 2, 1, 0.463647609);
cmp_angle(min, angle, best, bestoutx, bestouty, 1, 1, 0.785398163);
cmp_angle(min, angle, best, bestoutx, bestouty, 1, 1, 0.785398163);
cmp_angle(min, angle, best, bestoutx, bestouty, 0, 1, pi / 2);
// is line too short for TeX?
double len = sqrt(dx * dx + dy * dy);
if (len < 2) {
return 0; // ignore very short lines
}
if (bestoutx != 0 && bestouty != 0) {
double minlen = -1;
if (bestoutx < bestouty)
minlen = double(bestoutx) / double(bestouty);
else
minlen = double(bestouty) / double(bestoutx);
minlen = shortlinelen * sqrt(1.0 + minlen * minlen);
if (len < minlen) {
// too short
if (shortbezline) {
// either turn into bezier
lineerr.x = 0; lineerr.y = 0;
if (fprintf(file, "\n\\qbezier(%.4g,%.4g)(%.4g,%.4g)(%.4g,%.4g)",
latex_coord1(double(x0)), latex_coord1(double(y0)),
latex_coord1(double((x0 + x1) / 2)),
latex_coord1(double((y0 + y1) / 2)),
latex_coord1(double(x1)), latex_coord1(double(y1)))
== 0) return 1;
return 0;
} else {
// or make line longer
len = minlen;
}
}
}
if (dx < 0) { bestoutx = -bestoutx; best = pi - best; }
if (dy < 0) { bestouty = -bestouty; best = -best; }
// reposition line so error is minimal
double mx = latex_coord(double((x0 + x1) / 2)); // middle
double my = latex_coord(double((y0 + y1) / 2));
double wid = cos(best) * len;
mx -= wid / 2;
wid = dabs(wid);
my -= sin(best) * len / 2;
lineerr.x = coord_latex(mx) - x1;
lineerr.y = coord_latex(my) - y1;
if (bestoutx == 0) {
if (fprintf(file, "\n\\put(%.4g,%.4g){\\line(0,%d){%.4g}}",
round1(mx), round1(my), bestouty, round1(len))
== 0) return 1;
} else {
if (fprintf(file, "\n\\put(%.4g,%.4g){\\line(%d,%d){%.5g}}",
round1(mx), round1(my), bestoutx, bestouty, round2(wid))
== 0) return 1;
}
/*if (fprintf(file, "\n\\put(%.4g,%.4g){\\circle*{3}}",
(latex_coord(double(current.x))),
(latex_coord(double(current.y)))) == 0) return 1;*/
return 0;
}
//________________________________________
int Path::latex(FILE* file, const BBox& globbox) const
{
XY current;
// is path a box?
if (elements == 6 && points == 5 && element[0].type == move
&& element[1].type == draw && element[2].type == draw
&& element[3].type == draw && element[4].type == draw
&& element[5].type == close && point[0].near(point[4])) {
log(("Path::latex: is box?\n"));
bool hor; // true => next segment must be horizontal, otherwise vertical
if (iabs(point[1].x - point[0].x) > iabs(point[1].y - point[0].y))
hor = true;
else
hor = false;
bool valid = true;
int width = 0, height = 0, midx = 0, midy = 0;
int i = 0;
do { // horizontal and vertical lines must alternate
int dx = iabs(point[i+1].x - point[i].x);
int dy = iabs(point[i+1].y - point[i].y);
midx += point[i].x / 4; midy += point[i].y / 4;
if (hor) {
width += dx / 2;
} else {
height += dy / 2;
int tmp = dx; dx = dy; dy = tmp;
}
if (dy > dx / 12) valid = false;
hor = !hor;
} while (++i < 4);
if (valid) {
if (fprintf(file, "\n\\put(%.4g,%.4g){\\framebox(%.4g,%.4g){%s%s}}",
latex_coord1(double(midx - width / 2 - globbox.min.x)),
latex_coord1(double(midy - height / 2 - globbox.min.y)),
latex_coord1(double(width)),
latex_coord1(double(height)),
(label1 == 0 ? "" : label1),
(label1 == 0 ? "" : label2)) == 0) return 1;
label1 = 0; // "label was used"
return 0;
}
}
// is path a circle?
// 2 to 8 points, 1st and last point same, all beziers
if (elements >= 4 && elements <= 10
&& element[0].type == move
&& element[1].type == bezier && element[2].type == bezier
&& element[elements - 1].type == close
&& element[elements - 2].type == bezier
&& point[0].near(point[element[elements - 2].firstpoint + 2])) {
// check remaining points (if any)
int i;
XY m(0, 0);
for (i = 1; i < elements - 1; i++) {
if (element[i].type != bezier) break;
m.x += point[element[i].firstpoint + 2].x;
m.y += point[element[i].firstpoint + 2].y;
}
if (i >= elements - 1) {
m.x /= elements - 2;
m.y /= elements - 2;
double minr = 1e10, maxr = 0; // actually radius^2
double meanr = 0;
// measure mean radius (and get max and min)
for (i = 1; i < elements - 1; i++) {
double dx = double(point[element[i].firstpoint + 2].x - m.x);
double dy = double(point[element[i].firstpoint + 2].y - m.y);
double radius = dx * dx + dy * dy;
log(("Path::latex: circle? Point radius %f\n", radius));
if (minr > radius)
minr = radius;
else if (maxr < radius)
maxr = radius;
meanr += radius;
}
meanr /= double(elements - 2);
double r = latex_coord1(sqrt(meanr));
log(("Path::latex: circle? Mean radius %f pts\n", r));
if (meanr - minr < meanr / 8 && maxr - meanr < meanr / 8 && r < 40) {
if (fprintf(file, "\n\\put(%.4g,%.4g){\\circle{%.4g}}",
latex_coord1(double(m.x - globbox.min.x)),
latex_coord1(double(m.y - globbox.min.y)),
r * 2) == 0) return 1; else return 0;
} else {
log(("Path::latex: not circlish enough\n"));
}
}
}
// is neither box nor circle
bool linestart = true;
for (int i = 0; i < elements; i++) {
switch (element[i].type) {
case move:
linestart = true;
current = point[element[i].firstpoint];
current.x -= globbox.min.x; current.y -= globbox.min.y;
break;
case bezier: {
linestart = false;
XY* points = &point[element[i].firstpoint];
XY next = points[2];
next.x -= globbox.min.x; next.y -= globbox.min.y;
if (points[0].near(points[1])) {
XY control;
control.avg(points[0], points[1]);
control.x -= globbox.min.x; control.y -= globbox.min.y;
// control points are identical; use LaTeX's bezier
if (fprintf(file, "\n\\qbezier(%.4g,%.4g)(%.4g,%.4g)(%.4g,%.4g)",
latex_coord1(double(current.x)),
latex_coord1(double(current.y)),
latex_coord1(double(control.x)),
latex_coord1(double(control.y)),
latex_coord1(double(next.x)),
latex_coord1(double(next.y))) == 0) return 1;
} else {
// just draw straight line
if (Drawfile::latex_line(file, current.x, current.y, next.x, next.y))
return 1;
}
current = next;
break;
}
case draw: {
XY next = point[element[i].firstpoint];
next.x -= globbox.min.x; next.y -= globbox.min.y;
if (Drawfile::latex_line(file, current.x, current.y, next.x, next.y))
return 1;
if (linestart && sarrow && Drawfile::latex_arrowhead(file,
current.x - Drawfile::latex_lineerrx(),
current.y - Drawfile::latex_lineerry(),
atan2(current.y - next.y, current.x - next.x))) return 1;
linestart = false;
// for correct drawfiles, always at least one other tag follows 'draw'
if (earrow && i == elements - 1) {
if (Drawfile::latex_arrowhead(file,
next.x + Drawfile::latex_lineerrx(),
next.y + Drawfile::latex_lineerry(),
atan2(next.y - current.y, next.x - current.x))) return 1;
}
current = next;
break;
}
default:
linestart = false;
break; // do nothing
}
}
return 0;
}
//________________________________________
const char* Label::fontcmd(void) const
{
switch (parent->fontentry(fontnr)) {
case roman:
return "\\rmfamily ";
break;
case roman_i:
return "\\rmfamily\\itshape ";
break;
case roman_b:
return "\\rmfamily\\bfseries ";
break;
case roman_ib:
return "\\rmfamily\\itshape\\bfseries ";
break;
case sanss:
return "\\sffamily ";
break;
case sanss_i:
return "\\sffamily\\slshape ";
break;
case sanss_b:
return "\\sffamily\\bfseries ";
break;
case sanss_ib:
return "\\sffamily\\slshape\\bfseries ";
break;
case typew:
return "\\ttfamily ";
break;
case typew_i:
return "\\ttfamily\\slshape ";
break;
case typew_b:
return "\\ttfamily\\bfseries ";
break;
case typew_ib:
return "\\ttfamily\\slshape\\bfseries ";
break;
default:
return "";
break;
}
}
//____________________
int Label::latex(FILE* file, const BBox& globbox) const
{
if (fprintf(file, "\n\\put(%.4g,%.4g){%s%s}",
latex_coord1(bbox.min.x - globbox.min.x),
latex_coord1(bbox.min.y - globbox.min.y),
this->fontcmd(), text) == 0) return 1;
return 0;
}
//________________________________________
int Group::latex(FILE* file, const BBox& globbox) const
{
#if 0
if (fprintf(file, "\n\\put(%d,%d){\\dashbox{2}(%d,%d){}}",
latex_coord(bbox.min.x - globbox.min.x),
latex_coord(bbox.min.y - globbox.min.y),
latex_coord(bbox.max.x - bbox.min.x),
latex_coord(bbox.max.y - bbox.min.y))
== 0) return 1;
#endif
DrawObj* o = first;
if (first && (o = first->getnext()) && o->getnext() == 0) {
DrawObj* patho = 0;
DrawObj* labelo = 0;
if (typeid(*first) == typeid(Path)) patho = first;
else if (typeid(*o) == typeid(Path)) patho = o;
if (typeid(*first) == typeid(Label)) labelo = first;
else if (typeid(*o) == typeid(Label)) labelo = o;
if (patho && labelo) {
thinthick newthinlines = ((Path*)patho)->thinstate();
if (print_thinthick(file, newthinlines)) return 1;
Path::setlabel(((Label*)labelo)->fontcmd(),
((Label*)labelo)->getlabel());
if (((Path*)patho)->latex(file, globbox)) return 1;
if (((Path*)patho)->usedlabel()) return 0;
newthinlines = ((Label*)labelo)->thinstate();
if (print_thinthick(file, newthinlines)) return 1;
if (((Label*)labelo)->latex(file, globbox)) return 1;
return 0;
}
}
// output objects
o = first;
while (o) {
thinthick newthinlines = o->thinstate();
if (print_thinthick(file, newthinlines)) return 1;
if (o->latex(file, globbox)) return 1;
o = o->getnext();
}
return 0;
}
//________________________________________
/* when outputting text area, copy comments (after "\;") verbatim, ignore
rest of ASCII source */
int Textarea::latex(FILE* file, const BBox& globbox) const
{
char* curpos = text;
// curpos at the beginning of a new line (text always ends with \n!)
while (curpos[1] != 0) {
if (*++curpos == '\\' && curpos[1] == ';') {
// copy line to output, substituting for "#1" to "#8"
if (fputc('\n', file) == EOF) return 1;
curpos += 2;
do {
switch (*curpos) {
case '\\':
if (*++curpos == '\n') {
if (fputc('\\', file) == EOF) return 1;
} else {
if (fwrite(curpos - 1, 1, 2, file) == 0) return 1;
curpos++;
}
break;
case '#':
if (*++curpos >= '1' && *curpos <= '8') {
// substitute
coord val;
switch (*curpos++) {
case '1': val = bbox.min.x - globbox.min.x; break;
case '2': val = bbox.min.y - globbox.min.y; break;
case '3': val = bbox.max.x - globbox.min.x; break;
case '4': val = bbox.max.y - globbox.min.y; break;
case '5': val = bbox.max.x - bbox.min.x; break;
case '6': val = bbox.max.y - bbox.min.y; break;
case '7':
val = (bbox.min.x + bbox.max.x) / 2 - globbox.min.x; break;
case '8':
val = (bbox.min.y + bbox.max.y) / 2 - globbox.min.y; break;
default: val = 0;
}
if (fprintf(file, "%.4g", latex_coord1(val)) == 0) return 1;
} else {
if (fputc('#', file) == EOF) return 1;
}
break;
case '%': {
// TeX comment - don't substitute for rest of line
char* nlpos = curpos;
while (*++nlpos != '\n') ;
if (fwrite(curpos, 1, nlpos - curpos, file) == 0) return 1;
curpos = nlpos;
break;
}
default:
if (*curpos != '\n')
if (fputc(*curpos++, file) == EOF) return 1;
}
} while (*curpos != '\n');
} else {
// skip characters until next \n
while (*curpos++ != '\n') ;
curpos--;
}
}
return 0;
}
//______________________________________________________________________
static char outname[256]; // name of output file
int main (int argc, char* argv[])
{
argc--; argv++; // skip first word on command line
if (argc == 0) {
fprintf(stderr, "Syntax: draw2latex [-shortline] [-origin] <DrawFile> "
"[<DrawFile> ...]\n"
"Output is written to <DrawFile>/tex\n");
return 0;
}
int tmpargc = argc;
char** tmpargv = argv;
while (tmpargc) {
if (strcmp(*tmpargv, "-shortline") == 0 || strcmp(*tmpargv, "-s") == 0) {
*tmpargv[0] = '\0';
shortbezline = true;
} else if (strcmp(*tmpargv, "-origin") == 0
|| strcmp(*tmpargv, "-o") == 0) {
*tmpargv[0] = '\0';
usedrawbbox = false;
}
tmpargc--; tmpargv++;
}
while (argc) {
if (*argv[0] != '\0') {
FILE* file = fopen(*argv, "r");
if (file == 0) {
fprintf(stderr, "Couldn't open \"%s\"\n", *argv);
} else {
Drawfile* d = new Drawfile;
// load each file on command line
if (d->load(file))
fprintf(stderr, "Couldn't read \"%s\"\n", *argv);
fclose(file);
sprintf(outname, "%s/tex", *argv);
file = fopen(outname, "w");
if (file == 0) {
fprintf(stderr, "Couldn't open \"%s\"\n", outname);
} else {
// create global bounding box
d->make_bbox();
// write converted LaTeX
if (d->latex(file))
fprintf(stderr, "Couldn't write to \"%s\"\n", outname);
fclose(file);
_kernel_osfile_block kb;
kb.load = 0xaca;
_kernel_osfile(18, outname, &kb);
}
delete d;
}
}
argc--; argv++;
}
return 0;
}